/**
 * @Copyright:Copyright (c) 1992 - 2015
 * @Company: TengTong
 */
package com.tengtong.duang.commons.utils;

import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;

import com.tengtong.duang.commons.base.AssertUtils;
import com.tengtong.duang.commons.base.Callback;

/**
 * 反射工具类，是其他工具类的基础
 *
 * @author jai
 * @since: v1.0
 */
public class ReflectUtils {

	/**
	 * 将字符串（enum 名称）或 数字（enum 序号）转成 enum，还有一个更加灵活的就是使用 toString() 来对比转成 enum
	 *
	 * @param enumType	要转的 enum 类型
	 * @param value  值
	 * @return
	 */
	public static Enum getEnumBy(Class<?> enumType, String value)throws Exception{
		AssertUtils.notNull(enumType, "Enum type must not be null");
		Class cz = Class.forName(enumType.getName());
		Object[] results =  cz.getEnumConstants();
		if(results != null){
			for(Object result: results){
				Enum e = (Enum)result;
				if(e.name().equalsIgnoreCase(value) || 
						String.valueOf(e.ordinal()).equals(value) ||
						e.toString().equalsIgnoreCase(value))
					return e;
			}
		}
		return null;
	}
	
	/**
	 * 将反射时的checked exception转换为unchecked exception.
	 */
	public static RuntimeException toRuntimeException(Exception e){
		if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException
				|| e instanceof NoSuchMethodException) {
			return new IllegalArgumentException("Reflection Exception.", e);
		} else if (e instanceof InvocationTargetException) {
			return new RuntimeException("Reflection Exception.", ((InvocationTargetException) e).getTargetException());
		} else if (e instanceof RuntimeException) {
			return (RuntimeException) e;
		}
		return new RuntimeException("Unexpected Checked Exception.", e);
	}

	/**
	 * 判断类型是否能够实例化
	 *
	 * @param clazz
	 * @return
	 */
	public static boolean canNewInstance(Class<?> clazz){
		return !(clazz.isAnnotation() || clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers()));
	}
	
	/**
	 * 判断 target 是否继承（或实现）某类型 parentClazz，并且能够实例化
	 *
	 * @param target
	 * @param parentClazz
	 * @return
	 */
	public static boolean isInheritanceAndNewInstance(Class<?> target, Class<?> parentClazz){
		return isInheritance(target, parentClazz) && canNewInstance(target);
	}
	
	/**
	 * 判断对象是否某个类或子类的对象
	 *
	 * @param o     要判断的对象
	 * @param type  类型
	 * @return
	 */
	public static boolean isInheritance(Object o, Class<?> type){
		return type.isAssignableFrom(o.getClass());
	}
	
	/**
	 * 判断某类是否某个类或子类，或者实现某接口
	 *
	 * @param source 要判断的类
	 * @param type   
	 * @return
	 */
	public static boolean isInheritance(Class<?> source, Class<?> type){
		if(source == null || type == null)
			return false;
		return type.isAssignableFrom(source);
	}
	
	/**
	 * 从类中找出数据域。这个与  getAccessibleField 不同的是，当出现多个相同名称而不同类型的变量时，
	 * 
	 * 本方法更能准确找到所需要的数据域。
	 *
	 * @param clazz  要找的类
	 * @param name   要找的变量名称
	 * @param type   变量的类型
	 * @return
	 */
	public static Field findField(Class<?> clazz, String name, Class<?> type) {
		AssertUtils.notNull(clazz, "Class must not be null");
		AssertUtils.isTrue(name != null || type != null, "Either name or type of the field must be specified");
		Class<?> searchType = clazz;
		while (!Object.class.equals(searchType) && searchType != null) {
			Field[] fields = searchType.getDeclaredFields();
			for (Field field : fields) {
				if ((name == null || name.equals(field.getName())) && (type == null || type.equals(field.getType()))) {
					return field;
				}
			}
			searchType = searchType.getSuperclass();
		}
		return null;
	}

	/**
	 * 执行 Field "set" 方法
	 *
	 * @param obj
	 * @param propertyName
	 * @param values
	 * @throws Exception
	 */
	public static void invokeWriteMethod(Object obj, String propertyName, Object... values) throws Exception{
		PropertyDescriptor pd = new PropertyDescriptor(propertyName, obj.getClass());
		Method writeMethod = pd.getWriteMethod();
		
		writeMethod.invoke(obj, values);
	}
	
	/**
	 * 对类的所有方法（包换父类）进行过滤，并且在匹配的方法上执行一个回调动作。
	 * 
	 * 我们可以通过过滤器来过滤 Public，或包含某注解的方法，然后执行一些动作等
	 *
	 * @param clazz
	 * @param mc       回调接口，使用本接口可以让方法执行一些操作
	 * @param mf       过滤接口，用来过滤一些方法
	 * @throws IllegalArgumentException
	 */
	public static void doWithMethods(Class<?> clazz, Callback<Method> mc, Predicate<Method> mf)
			throws IllegalArgumentException {

		// Keep backing up the inheritance hierarchy.
		Method[] methods = clazz.getDeclaredMethods();
		for (Method method : methods) {
			if (mf != null && !mf.test(method)) {
				continue;
			}
			try {
				mc.doWith(method);
			}
			catch (Exception ex) {
				throw new IllegalStateException("Shouldn't be illegal to access method '" + method.getName()
						+ "': " + ex);
			}
		}
		if (clazz.getSuperclass() != null) {
			doWithMethods(clazz.getSuperclass(), mc, mf);
		}
		else if (clazz.isInterface()) {
			for (Class<?> superIfc : clazz.getInterfaces()) {
				doWithMethods(superIfc, mc, mf);
			}
		}
	}	
	
	/**
	 * 不需要过滤器的情况下使用
	 *
	 * @param clazz
	 * @param mc
	 * @throws IllegalArgumentException
	 */
	public static void doWithMethods(Class<?> clazz, Callback<Method> mc) throws IllegalArgumentException {
		doWithMethods(clazz, mc, null);
	}
	
	/**
	 * 对类的所有属性进行过滤（使用过滤器 bf），对符合条件的 Field 执行 callback 动作。
	 *
	 * @param clazz
	 * @param cb
	 * @param bf
	 * @throws IllegalArgumentException
	 */
	public static void doWithFields(Class<?> clazz, Callback<Field> cb, Predicate<Field> bf) throws IllegalArgumentException{
		Class<?> targetClass = clazz;
		
		do {
			Field[] fields = targetClass.getDeclaredFields();
			for(Field field: fields){
				if(bf != null && !bf.test(field)){
					continue;
				}
				
				try{
					cb.doWith(field);
				}catch(Exception ex){
					throw new IllegalStateException(
							"Shouldn't be illegal to access field '" + field.getName() + "': " + ex);
				}
			}
			targetClass = targetClass.getSuperclass();
			
		}while (targetClass != null && targetClass != Object.class);
		
	}
	
	/**
	 * 只要 Callback 的版本
	 *
	 * @param clazz
	 * @param cb
	 * @throws IllegalArgumentException
	 */
	public static void doWithFields(Class<?> clazz, Callback<Field> cb) throws IllegalArgumentException{
		
		doWithFields(clazz, cb, null);
	}
	
	/**
	 * 对类的属性进行 遍历处理，如果 Callback 与  BaseFilter 同时存在，则先做 BaseFielter，对于 accept 的属性再做 callback
	 *
	 * @param clazz
	 * @param cb         一个对属性进行 操作的回调
	 * @param bf         一个对属性进行过滤的操作
	 * @throws IntrospectionException
	 */
	public static void doWithPropertys(Class<?> clazz, Callback<PropertyDescriptor> cb, Predicate<PropertyDescriptor> bf) 
						throws IntrospectionException, IllegalStateException {
		PropertyDescriptor[] ps = Introspector.getBeanInfo(clazz).getPropertyDescriptors();
		if(ps != null && ps.length > 0){
			for(PropertyDescriptor p: ps){
				if((p == null) || (bf != null && !bf.test(p)))
					continue;
				
				if(cb != null){
					try{
						cb.doWith(p);
					}catch(Exception ex){
						throw new IllegalStateException("Shouldn't be illegal to access field '" + p.getName() + "':" + ex);
					}
				}
			}
		}
	}
	
	/**
	 * 参见 {@link #doWithPropertys(Class<?>, Callback<PropertyDescriptor>, BaseFilter<PropertyDescriptor>)}
	 * 
	 * 本方法是它的 只有 Callback 的版本
	 *
	 * @param clazz
	 * @param cb
	 * @throws IntrospectionException
	 * @throws IllegalStateException
	 */
	public static void doWithPropertys(Class<?> clazz, Callback<PropertyDescriptor> cb)	throws IntrospectionException, IllegalStateException {
		doWithPropertys(clazz, cb, null);
	}
	
	/**
	 * 获取所有方法，包括父类。
	 *
	 * @param leafClass
	 * @return
	 * @throws IllegalArgumentException
	 */
	public static Method[] getAllDeclaredMethods(Class<?> leafClass) throws IllegalArgumentException {
		final List<Method> methods = new ArrayList<Method>(32);
		doWithMethods(leafClass, new Callback<Method>() {
			@Override
			public void doWith(Method method) {
				methods.add(method);
			}
		});
		return methods.toArray(new Method[methods.size()]);
	}
	
	/**
	 * 获取类所有公有方法，包括父类。
	 *
	 * @param leafClass
	 * @return
	 * @throws IllegalArgumentException
	 */
	public static Method[] getAllPublicMethods(Class<?> leafClass) throws IllegalArgumentException {
		final List<Method> methods = new ArrayList<Method>(32);
		doWithMethods(leafClass, new Callback<Method>(){
			@Override
			public void doWith(Method method) {
				if(Modifier.isPublic(method.getModifiers()) && Modifier.isPublic(method.getDeclaringClass().getModifiers())
						&& !method.isBridge())
					methods.add(method);
			}
		});
		return methods.toArray(new Method[methods.size()]);
	}
	
	/**
	 * 获取类所有可修改属性。。要注意，是属性，不是类的任意数据成员变量
	 *
	 * @param targetClass  
	 * @return             如果没有可修改属性，则返回一个空集（不是 null）
	 * @throws IllegalArgumentException
	 */
	public static Field[] getAllCanModifiedProperty(final Class<?> targetClass) throws IllegalArgumentException{
		
		final List<Field> fields = new ArrayList<Field>(32);
		
		doWithFields(targetClass, new Callback<Field>(){

			@Override
			public void doWith(Field t) {
				fields.add(t);
			}
			
		}, COPYABLE_FIELDS);
		
		return fields.toArray(new Field[fields.size()]);
	}

	/**
	 * 过滤掉所有静态域，final 域
	 */
	public static Predicate<Field> COPYABLE_FIELDS = new Predicate<Field>() {

		@Override
		public boolean test(Field field) {
			return !(Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers()));
		}
	};

	public static boolean isCopyableField(Field field){
		return !(Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers()));
	}
	
	/**
	 * Pre-built MethodFilter that matches all non-bridge methods.
	 */
	public static Predicate<Method> NON_BRIDGED_METHODS = new Predicate<Method>() {

		@Override
		public boolean test(Method method) {
			return !method.isBridge();
		}
	};


	/**
	 * Pre-built MethodFilter that matches all non-bridge methods
	 * which are not declared on <code>java.lang.Object</code>.
	 */
	public static Predicate<Method> USER_DECLARED_METHODS = new Predicate<Method>() {

		@Override
		public boolean test(Method method) {
			return (!method.isBridge() && method.getDeclaringClass() != Object.class);
		}
	};
	
}
